#include <config.h>
#include <debug.h>
#include <header.h>
#include <rules.h>
#include <xml_util.h>
#include <md5.h>

#include "gui.h"
#include "language.h"
#include "xml_util.h"

#define PROMPT_ICON_SIZE 32

struct packetid_and_path
{
  ipq_id_t packet_id;
  char path[_POSIX_PATH_MAX];
};
struct dialog_info
{
  struct list_head list;
  char path[_POSIX_PATH_MAX];
};

static LIST_HEAD(dialog_head);
static int is_dialog_exist(char *path);
static void mark_dialog_exist(char *path);
static void unmark_dialog_exist(char *path);
static void func_for_response(GtkDialog *dialog, gint response_id, gpointer user_data);
static void more_expander_callback(GObject    *object,
                                   GParamSpec *param_spec,
                                   gpointer    user_data);

static void func_for_warnresponse(GtkDialog *dialog, gint response_id, gpointer user_data);


void popup_query_dialog(char *path, pid_t pid, ipq_packet_msg_t *m)
{
  GtkWidget *dialog = NULL;

  GtkWidget *hbox1 = NULL;/*common header*/
  GtkWidget *hbox2 = NULL;/*program infomation*/

  GdkPixbuf *coolnet_icon_pixbuf = NULL;
  GtkWidget *coolnet_icon = NULL;
  GtkWidget *header_label = NULL; /*common header content*/
  GdkPixbuf *program_icon_pixbuf = NULL;
  GtkWidget *program_icon = NULL;
  GtkWidget *program_info_vbox = NULL;/*show program information*/

  GtkWidget *name_hbox = NULL;
  GtkWidget *path_hbox = NULL;
  GtkWidget *pid_hbox = NULL;
  GtkWidget *expander_hbox = NULL;

  GtkWidget *more_info_vbox = NULL;

  GtkWidget *name_label = NULL;
  GtkWidget *path_label = NULL;
  GtkWidget *pid_label = NULL;
  GtkWidget *expander = NULL;

  GtkWidget *program_name_label = NULL;
  GtkWidget *program_path_label = NULL;
  GtkWidget *program_pid_label = NULL;

  struct packetid_and_path *data = NULL;

  char *mark = NULL;

  GError *err = NULL;

  char program_pid[6] = {0};
  char name[128] = {0};
  char icon_path[256] = {0};
  char md5sum_now[MD5SUM_LEN+1]={0};

  int local_port = 0, remote_port = 0; 
  int protocol = 0, hook = 0;
  struct in_addr local_addr;
  struct in_addr remote_addr;
  struct iphdr *ip = NULL;
  struct tcphdr *tcp = NULL;
  char from[128] = {0};
  char to[128] = {0};
  char md5[128] = {0};

  if (NULL == path)
  {
    return;
  }

  hook = m->hook;
  ip = (struct iphdr *)m->payload;
  protocol = ip->protocol;
  /*Udp and tcp header's first 32 bit has the same meaning*/
  tcp = (struct tcphdr *)(m->payload + 4 * ip->ihl);
  if (NF_IP_LOCAL_OUT == hook)
  {
    local_addr.s_addr = ip->saddr;
    remote_addr.s_addr = ip->daddr;
    local_port = htons(tcp->source);
    remote_port = htons(tcp->dest);
  }
  else if (NF_IP_LOCAL_IN == hook)
  {
    local_addr.s_addr = ip->daddr;
    remote_addr.s_addr = ip->saddr;
    local_port = htons(tcp->dest);
    remote_port = htons(tcp->source);
  }

  md_buffer((void *)path, strlen(path), (unsigned char *)md5sum_now);

  sprintf(icon_path, "%s/%s.png", APP_ICON_PATH, md5sum_now);

  __GUI_DEBUG("icon_path:%s\n", icon_path);

  mark = strrchr(path, '/');
  strcpy(name, mark + 1);

  sprintf(program_pid, "%d", pid);
  data = malloc(sizeof(struct packetid_and_path));
  if (NULL == data)
  {
    __ERROR_MSG("Error when malloc\n");
    return;
  }
  memset(data, 0, sizeof(struct packetid_and_path));
  data->packet_id = m->packet_id;
  strncpy(data->path, path, _POSIX_PATH_MAX - 1);
  if (is_dialog_exist(path))
  {
    return;
  }
  else
  {
    mark_dialog_exist(path);
  }
  gdk_threads_enter();
  dialog = gtk_dialog_new_with_buttons(TEXT_COOLNET_SECURITY_ALERT, NULL, 0,
                                       TEXT_ALLOW, GTK_RESPONSE_YES,
                                       TEXT_DENY, GTK_RESPONSE_NO, NULL);
  /*pack two hbox*/
  hbox1 = gtk_hbox_new(FALSE, 0);
  gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), hbox1, FALSE, FALSE, 10);
  hbox2 = gtk_hbox_new(FALSE, 0);
  gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), hbox2, FALSE, FALSE, 5);

  /*pack header icon*/
  coolnet_icon_pixbuf = gdk_pixbuf_new_from_file(COOLNET_ALERT_ICON, NULL);
  coolnet_icon = gtk_image_new_from_pixbuf(coolnet_icon_pixbuf);
  gtk_box_pack_start(GTK_BOX(hbox1), coolnet_icon, FALSE, FALSE, 5);
  /*pack header content*/
  header_label = gtk_label_new(TEXT_COOLNET_ALERT_HEADER);
  gtk_label_set_line_wrap(GTK_LABEL(header_label), TRUE);
  gtk_box_pack_start(GTK_BOX(hbox1), header_label, FALSE, FALSE, 5);

  /*program icon*/
  program_icon_pixbuf = get_program_icon_by_pid(pid);

  if (NULL == program_icon_pixbuf)
  {
    program_icon_pixbuf = get_program_icon_by_desktop_entry(name);
  }

  if (NULL == program_icon_pixbuf)
  {
    program_icon_pixbuf = gdk_pixbuf_new_from_file(DEFAULT_PROGRAM_ICON, NULL);
  }
  else
  {
    /*save icon*/
    if (-1 == access(icon_path, 0))/*file not exist*/
    {
      gdk_pixbuf_save(program_icon_pixbuf, icon_path, "png", &err, NULL); 
    }
  }

  program_icon_pixbuf = gdk_pixbuf_scale_simple(program_icon_pixbuf,
                                                PROMPT_ICON_SIZE,
                                                PROMPT_ICON_SIZE,
                                                GDK_INTERP_HYPER);

  program_icon = gtk_image_new_from_pixbuf(program_icon_pixbuf);
  gtk_box_pack_start(GTK_BOX(hbox2), program_icon, FALSE, FALSE, 5);

  program_info_vbox = gtk_vbox_new(FALSE, 0);
  gtk_box_pack_start(GTK_BOX(hbox2), program_info_vbox, FALSE, FALSE, 5);

  name_hbox = gtk_hbox_new(FALSE, 0);
  gtk_box_pack_start(GTK_BOX(program_info_vbox), name_hbox, FALSE, FALSE, 5);

  path_hbox = gtk_hbox_new(FALSE, 0);
  gtk_box_pack_start(GTK_BOX(program_info_vbox), path_hbox, FALSE, FALSE, 5);

  pid_hbox = gtk_hbox_new(FALSE, 0);
  gtk_box_pack_start(GTK_BOX(program_info_vbox), pid_hbox, FALSE, FALSE, 5);

  expander_hbox = gtk_hbox_new(FALSE, 0);
  gtk_box_pack_start(GTK_BOX(program_info_vbox), expander_hbox, FALSE, FALSE, 5);


  more_info_vbox = gtk_vbox_new(FALSE, 0);
  gtk_box_pack_start(GTK_BOX(program_info_vbox), more_info_vbox, FALSE, FALSE, 0);

  name_label = gtk_label_new(TEXT_NAME);
  gtk_box_pack_start(GTK_BOX(name_hbox), name_label, FALSE, FALSE, 0);
  program_name_label = gtk_label_new(name);
  gtk_box_pack_start(GTK_BOX(name_hbox), program_name_label, FALSE, FALSE, 0);

  path_label = gtk_label_new(TEXT_PATH);
  gtk_box_pack_start(GTK_BOX(path_hbox), path_label, FALSE, FALSE, 0);
  program_path_label = gtk_label_new(path);
  gtk_box_pack_start(GTK_BOX(path_hbox), program_path_label, FALSE, FALSE, 0);

  pid_label = gtk_label_new(TEXT_PID);
  gtk_box_pack_start(GTK_BOX(pid_hbox), pid_label, FALSE, FALSE, 0);
  program_pid_label = gtk_label_new(program_pid);
  gtk_box_pack_start(GTK_BOX(pid_hbox), program_pid_label, FALSE, FALSE, 0);

  expander = gtk_expander_new(TEXT_MORE_INFO);
  gtk_expander_set_expanded(GTK_EXPANDER(expander), FALSE);
  gtk_box_pack_start(GTK_BOX(expander_hbox), expander, FALSE, FALSE, 0);

  /*pack more info in more_info_vbox*/
  GtkWidget *local_addr_hbox = gtk_hbox_new(FALSE, 0);
  sprintf(from, "%s%s:%d\n", TEXT_FROM_ADDR, inet_ntoa(local_addr), local_port);
  GtkWidget *from_label = gtk_label_new(from);
  gtk_box_pack_start(GTK_BOX(local_addr_hbox), from_label, FALSE, FALSE, 0);

  GtkWidget *remote_addr_hbox = gtk_hbox_new(FALSE, 0);
  sprintf(to, "%s%s:%d\n", TEXT_TO_ADDR, inet_ntoa(remote_addr), remote_port);
  GtkWidget *to_label = gtk_label_new(to);
  gtk_box_pack_start(GTK_BOX(remote_addr_hbox), to_label, FALSE, FALSE, 0); 

  GtkWidget *md5_hbox = gtk_hbox_new(FALSE, 0);
  sprintf(md5, "%s%s", TEXT_MD5, md5sum_now);
  GtkWidget *md5_label = gtk_label_new(md5);
  gtk_box_pack_start(GTK_BOX(md5_hbox), md5_label, FALSE, FALSE, 0); 

  gtk_box_pack_start(GTK_BOX(more_info_vbox), local_addr_hbox, FALSE, FALSE, 1); 
  gtk_box_pack_start(GTK_BOX(more_info_vbox), remote_addr_hbox, FALSE, FALSE, 1); 
  gtk_box_pack_start(GTK_BOX(more_info_vbox), md5_hbox, FALSE, FALSE, 1); 

  /*adjust some attr*/
  gtk_window_set_icon_from_file(GTK_WINDOW(dialog), COOLNET_TRAY_ICON, NULL);
  gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_CENTER);
  gtk_window_set_default_size(GTK_WINDOW(dialog), 200, 120);
  gtk_window_activate_default(GTK_WINDOW(dialog));
  gtk_widget_set_no_show_all(more_info_vbox, TRUE);

  /*connect signal*/
  gtk_signal_connect(GTK_OBJECT(dialog), "response", G_CALLBACK(func_for_response), 
                     (gpointer)data);

  g_signal_connect(G_OBJECT(expander), "notify::expanded",
                   G_CALLBACK(more_expander_callback), more_info_vbox);

  gtk_widget_show_all(dialog);

  gdk_threads_leave();

}
static void func_for_response(GtkDialog *dialog, gint response_id, gpointer user_data)
{
  struct packetid_and_path *p = (struct packetid_and_path*)user_data;
  if (GTK_RESPONSE_YES == response_id)
  {
    /*Add entry in configure file*/
    xml_allow_program((xmlChar *)p->path);
    add_general_list_store(p->path, 1);/*1 means allow*/
  }
  else if (GTK_RESPONSE_NO == response_id)
  {
    /*Add deny entry in configure file*/
    xml_deny_program((xmlChar *)p->path);
    add_general_list_store(p->path, 0);/*0 means deny*/
  }
  /*destroy the dialog*/
  gtk_widget_destroy(GTK_WIDGET(dialog));
  /*delete dialog mark*/
  unmark_dialog_exist(p->path);
  if (p != NULL)
  {
    free(p);
  }
}

//zyh add 
void popup_md5warn_dialog(char *path, pid_t pid, ipq_id_t packet_id)
{
  GtkWidget *dialog = NULL;

  GtkWidget *hbox1 = NULL;/*common header*/
  GtkWidget *hbox2 = NULL;/*program infomation*/

  GdkPixbuf *coolnet_icon_pixbuf = NULL;
  GtkWidget *coolnet_icon = NULL;
  GtkWidget *header_label = NULL; /*common header content*/
  GdkPixbuf *program_icon_pixbuf = NULL;
  GtkWidget *program_icon = NULL;
  GtkWidget *program_info_vbox = NULL;/*show program information*/

  GtkWidget *name_hbox = NULL;
  GtkWidget *path_hbox = NULL;
  GtkWidget *pid_hbox = NULL;

  GtkWidget *name_label = NULL;
  GtkWidget *path_label = NULL;
  GtkWidget *pid_label = NULL;
  GtkWidget *program_name_label = NULL;
  GtkWidget *program_path_label = NULL;
  GtkWidget *program_pid_label = NULL;

  struct packetid_and_path *data = NULL;

  char *mark = NULL;

  char program_pid[6] = {0};
  char name[128] = {0};
  if (NULL == path)
  {
    return;
  }
  mark = strrchr(path, '/');
  strcpy(name, mark + 1);

  sprintf(program_pid, "%d", pid);
  data = malloc(sizeof(struct packetid_and_path));
  if (NULL == data)
  {
    __ERROR_MSG("Error when malloc\n");
    return;
  }
  memset(data, 0, sizeof(struct packetid_and_path));
  data->packet_id = packet_id;
  strncpy(data->path, path, _POSIX_PATH_MAX - 1);
  if (is_dialog_exist(path))
  {
    return;
  }
  else
  {
    mark_dialog_exist(path);
  }
  gdk_threads_enter();
  dialog = gtk_dialog_new_with_buttons(TEXT_COOLNET_SECURITY_ALERT, NULL, 0,
                                       TEXT_ALLOW, GTK_RESPONSE_YES,
                                       TEXT_DENY, GTK_RESPONSE_NO, NULL);
  /*pack tow hbox*/
  hbox1 = gtk_hbox_new(FALSE, 0);
  gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), hbox1, FALSE, FALSE, 10);
  hbox2 = gtk_hbox_new(FALSE, 0);
  gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), hbox2, FALSE, FALSE, 5);

  /*pack header icon*/
  coolnet_icon_pixbuf = gdk_pixbuf_new_from_file(COOLNET_ALERT_ICON, NULL);
  coolnet_icon = gtk_image_new_from_pixbuf(coolnet_icon_pixbuf);
  gtk_box_pack_start(GTK_BOX(hbox1), coolnet_icon, FALSE, FALSE, 5);
  /*pack header content*/
  //I just change a little,can we make it a parm?
  header_label = gtk_label_new(TEXT_COOLNET_DIGESTALERT_HEADER);

  gtk_label_set_line_wrap(GTK_LABEL(header_label), 1);
  gtk_box_pack_start(GTK_BOX(hbox1), header_label, FALSE, FALSE, 5);

  /*program icon*/
  program_icon_pixbuf = get_program_icon_by_pid(pid);

  if (NULL == program_icon_pixbuf)
  {
    program_icon_pixbuf = gdk_pixbuf_new_from_file(DEFAULT_PROGRAM_ICON, NULL);
  }

  program_icon = gtk_image_new_from_pixbuf(program_icon_pixbuf);
  gtk_box_pack_start(GTK_BOX(hbox2), program_icon, FALSE, FALSE, 5);

  program_info_vbox = gtk_vbox_new(FALSE, 0);
  gtk_box_pack_start(GTK_BOX(hbox2), program_info_vbox, FALSE, FALSE, 5);

  name_hbox = gtk_hbox_new(FALSE, 0);
  gtk_box_pack_start(GTK_BOX(program_info_vbox), name_hbox, FALSE, FALSE, 5);

  path_hbox = gtk_hbox_new(FALSE, 0);
  gtk_box_pack_start(GTK_BOX(program_info_vbox), path_hbox, FALSE, FALSE, 5);

  pid_hbox = gtk_hbox_new(FALSE, 0);
  gtk_box_pack_start(GTK_BOX(program_info_vbox), pid_hbox, FALSE, FALSE, 5);

  name_label = gtk_label_new(TEXT_NAME);
  gtk_box_pack_start(GTK_BOX(name_hbox), name_label, FALSE, FALSE, 0);
  program_name_label = gtk_label_new(name);
  gtk_box_pack_start(GTK_BOX(name_hbox), program_name_label, FALSE, FALSE, 0);

  path_label = gtk_label_new(TEXT_PATH);
  gtk_box_pack_start(GTK_BOX(path_hbox), path_label, FALSE, FALSE, 0);
  program_path_label = gtk_label_new(path);
  gtk_box_pack_start(GTK_BOX(path_hbox), program_path_label, FALSE, FALSE, 0);

  pid_label = gtk_label_new(TEXT_PID);
  gtk_box_pack_start(GTK_BOX(pid_hbox), pid_label, FALSE, FALSE, 0);
  program_pid_label = gtk_label_new(program_pid);
  gtk_box_pack_start(GTK_BOX(pid_hbox), program_pid_label, FALSE, FALSE, 0);
  /*adjust some attr*/
  gtk_window_set_icon_from_file(GTK_WINDOW(dialog), COOLNET_TRAY_ICON, NULL);
  gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_CENTER);
  gtk_window_set_default_size(GTK_WINDOW(dialog), 200, 120);
  gtk_window_activate_default(GTK_WINDOW(dialog));
  /*connect signal*/
  gtk_signal_connect(GTK_OBJECT(dialog), "response", G_CALLBACK(func_for_warnresponse), 
                     (gpointer)data);
  gtk_widget_show_all(dialog);

  gdk_threads_leave();

}

/* zyh add */
static void func_for_warnresponse(GtkDialog *dialog, gint response_id, gpointer user_data)
{
  struct packetid_and_path *p = (struct packetid_and_path*)user_data;

  if (GTK_RESPONSE_YES == response_id)
  {
    /* Update MD5 digest record */
    xml_updatemd5_program((xmlChar *)p->path);

  }
  else if (GTK_RESPONSE_NO == response_id)
  {
    /* Make it denied */
    xml_deny_program((xmlChar *)p->path);
  }
  /*destroy the dialog*/
  gtk_widget_destroy(GTK_WIDGET(dialog));
  /*delete dialog mark*/
  unmark_dialog_exist(p->path);
  if (p != NULL)
  {
    free(p);
  }
}

static int is_dialog_exist(char *path)
{
  int ret = 0;
  struct dialog_info *dialog = NULL;
  struct list_head *p = NULL;
  list_for_each(p, &dialog_head)
  {
    dialog = list_entry(p, struct dialog_info, list);
    if (strcmp(path, dialog->path) == 0)
    {
      ret = 1;
      goto out;
    }
  }
  out:
  return ret;
}

static void mark_dialog_exist(char *path)
{
  struct dialog_info *dialog = NULL;
  if (is_dialog_exist(path))
  {
    return;
  }
  dialog = malloc(sizeof(struct dialog_info));
  memset(dialog, 0, sizeof(struct dialog_info));
  strcpy(dialog->path, path);
  list_add(&dialog->list, &dialog_head);
}

static void unmark_dialog_exist(char *path)
{
  struct dialog_info *dialog = NULL;
  struct list_head *p = NULL;
  if (!is_dialog_exist(path))
  {
    return;
  }
  list_for_each(p, &dialog_head)
  {
    dialog = list_entry(p, struct dialog_info, list);
    if (strcmp(path, dialog->path) == 0)
    {
      list_del(&dialog->list);
      free(dialog);
      return;
    }
  }
}

static void more_expander_callback(GObject    *object,
                                   GParamSpec *param_spec,
                                   gpointer    user_data)
{
  GtkExpander *expander = NULL;
  GtkWidget *content_widget = NULL;
  GtkWidget *dialog = NULL;

  expander = GTK_EXPANDER(object);
  content_widget = GTK_WIDGET(user_data);

  if (gtk_expander_get_expanded(expander))
  {
    /* Show or create widgets */
    gtk_widget_set_no_show_all(content_widget, FALSE);
    gtk_widget_show_all(content_widget);
  }
  else
  {
    /* Hide or destroy widgets */
    gtk_widget_hide(content_widget);
    dialog = gtk_widget_get_ancestor(content_widget, GTK_TYPE_WINDOW);;
    gtk_window_resize(GTK_WINDOW(dialog), 1, 1);
  }
}
